#include "../../macro.h"
#include <iostream>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>

using std::cout;
using std::endl;

void server(char **argv)
{
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    ERR_CHECK(sfd, -1, "socket")

    sockaddr_in ser_addr{};
    ser_addr.sin_port = htons(atoi(argv[2]));
    // ser_addr.sin_port = htons(2000);
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_addr.s_addr = inet_addr(argv[1]);
    // ser_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    int ret = bind(sfd, (sockaddr *)&ser_addr, sizeof(sockaddr_in));
    ERR_CHECK(ret, -1, "bind")

    int opt = 1;
    ret = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    ERR_CHECK(ret, -1, "setsockopt");
    opt = 1;
    ret = setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
    ERR_CHECK(ret, -1, "setsockopt");

    ret = listen(sfd, 10);
    ERR_CHECK(ret, -1, "listen")

    fd_set rdset{}, tmp_set{};
    FD_SET(fileno(stdin), &rdset);
    FD_SET(sfd, &rdset);
    char buf[1024];
    int selectfd = sfd + 1, newfd{};

    while (1)
    {
        bzero(buf, 1024);
        memcpy(&tmp_set, &rdset, sizeof(tmp_set));

        ret = select(selectfd, &tmp_set, nullptr, nullptr, 0);
        ERR_CHECK(ret, -1, "select")
        if (FD_ISSET(sfd, &tmp_set))
        {
            socklen_t len = sizeof(ser_addr);
            newfd = accept(sfd, (sockaddr *)&ser_addr, &len);
            ERR_CHECK(newfd, -1, "accept")
            cout << "connect: " << inet_ntoa(ser_addr.sin_addr) << endl;
            FD_SET(newfd, &rdset);
            selectfd++;
        }

        if (FD_ISSET(STDIN_FILENO, &tmp_set))
        {
            // 踩坑处：这里的read不能换成recv， recv专门为socket使用，read更通用，但recv可以有其他设置
            ret = read(STDIN_FILENO, buf, sizeof(buf));
            ERR_CHECK(ret, -1, "recv")

            ret = send(newfd, buf, strlen(buf) - 1, 0);
            ERR_CHECK(ret, -1, "send")
        }

        if (FD_ISSET(newfd, &tmp_set))
        {
            ret = recv(newfd, buf, sizeof(buf), MSG_PEEK);
            ERR_CHECK(ret, -1, "recv")

            if (ret > 0)
                cout << "recv: " << buf << endl;
            else
            {
                cout << inet_ntoa(ser_addr.sin_addr) << "disconnet..." << endl;
                FD_CLR(newfd, &rdset);
            }
        }
    }
}

int main(int args, char *argv[])
{
    server(argv);
    return 0;
}